package com.michael.ims.gateway.route;

import com.michael.ims.gateway.component.WebClientTemplate;
import com.michael.ims.gateway.config.SystemConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@EnableScheduling
@Component
@Slf4j
public class DynamicRoute implements ApplicationContextAware {
    @Autowired
    private WebClientTemplate webClientTemplate;
    @Autowired
    private RouteDefinitionRepository inMemoryRouteDefinitionRepository;
    private ApplicationContext applicationContext;

    @Scheduled(fixedRate = 50000)
    public void route() {
        //从远程加载系统
        Mono<List<DynamicRouteEntity>> dynamicRouteMono = webClientTemplate.HandleGetList("/system/dynamic-route", DynamicRouteEntity.class);
        //保存->inMemoryRouteDefinitionRepository
        Flux<DynamicRouteEntity> dynamicRouteEntityFlux = dynamicRouteMono.flatMapMany(Flux::fromIterable);
        dynamicRouteEntityFlux
                .flatMap(dynamicRouteEntity -> {
                    RouteDefinition routeDefinition = new RouteDefinition();
                    routeDefinition.setId(dynamicRouteEntity.getId() + "");
                    routeDefinition.setOrder(dynamicRouteEntity.getId());
                    routeDefinition.setUri(URI.create(dynamicRouteEntity.getUrl()));

                    //filter
                    List<FilterDefinition> filterDefinitions = new ArrayList<>();
                    //RewritePath
                    FilterDefinition rewritePathFilter = new FilterDefinition("RewritePath="+SystemConfig.CONTEXT_PATH + "/(?<segment>.*),/${segment}");
                    filterDefinitions.add(rewritePathFilter);
//                    //writeHeader
//                    FilterDefinition setRequestHeader = new FilterDefinition("SetRequestHeader=authorization,michael");
//                    filterDefinitions.add(setRequestHeader);
                    routeDefinition.setFilters(filterDefinitions);


                    //predicate
                    List<PredicateDefinition> predicateDefinitions = new ArrayList<>();
                    PredicateDefinition pathPredicate = new PredicateDefinition();
                    pathPredicate.setName("Path");
                    Map<String, String> predicateArgs = new HashMap<>();
                    predicateArgs.put(NameUtils.generateName(1), SystemConfig.CONTEXT_PATH + "/" + dynamicRouteEntity.getContext() + "/**");
                    pathPredicate.setArgs(predicateArgs);
                    predicateDefinitions.add(pathPredicate);
                    routeDefinition.setPredicates(predicateDefinitions);
                    return inMemoryRouteDefinitionRepository.save(Mono.just(routeDefinition));
                })
                .then(Mono.defer(() -> {
                    //刷新路由
                    applicationContext.publishEvent(new RefreshRoutesEvent(dynamicRouteEntityFlux));
                    return Mono.empty();
                }))
                .subscribe();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
